<!DOCTYPE html>
<html>
<!--
Test adapted from https://mxr.mozilla.org/chromium/source/src/third_party/WebKit/LayoutTests/fast/dom/TreeWalker/script-tests/acceptNode-filter.js
-->
<head>
<title>TreeWalker: acceptNode-filter</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="traversal-support.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<div id=log></div>
</head>
<body>
<p>Test JS objects as NodeFilters</p>
<script>
var testElement;
setup(function() {
    testElement = document.createElement("div");
    testElement.id = 'root';
    //testElement.innerHTML='<div id="A1"><div id="B1"></div><div id="B2"></div></div>';

    // XXX for Servo, build the tree without using innerHTML
    var a1 = document.createElement("div");
    a1.id = "A1";
    var b1 = document.createElement("div");
    b1.id = "B1";
    var b2 = document.createElement("div");
    b2.id = "B2";
    testElement.appendChild(a1);
    a1.appendChild(b1);
    a1.appendChild(b2);
});

test(function()
{
    function filter(node)
    {
        if (node.id == "B1")
            return /*NodeFilter.*/FILTER_SKIP;
        return /*NodeFilter.*/FILTER_ACCEPT;
    }

    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, filter);
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
    assert_node(walker.currentNode, { type: Element, id: 'A1' });
    assert_node(walker.nextNode(), { type: Element, id: 'B2' });
    assert_node(walker.currentNode, { type: Element, id: 'B2' });
}, 'Testing with raw function filter');

test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, {
        acceptNode : function(node) {
            if (node.id == "B1")
                return /*NodeFilter.*/FILTER_SKIP;
            return /*NodeFilter.*/FILTER_ACCEPT;
        }
    });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
    assert_node(walker.currentNode, { type: Element, id: 'A1' });
    assert_node(walker.nextNode(), { type: Element, id: 'B2' });
    assert_node(walker.currentNode, { type: Element, id: 'B2' });
}, 'Testing with object filter');

test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, null);
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
    assert_node(walker.currentNode, { type: Element, id: 'A1' });
    assert_node(walker.nextNode(), { type: Element, id: 'B1' });
    assert_node(walker.currentNode, { type: Element, id: 'B1' });
}, 'Testing with null filter');

test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, undefined);
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
    assert_node(walker.currentNode, { type: Element, id: 'A1' });
    assert_node(walker.nextNode(), { type: Element, id: 'B1' });
    assert_node(walker.currentNode, { type: Element, id: 'B1' });
}, 'Testing with undefined filter');

// XXX Servo breaks the test when a callback isn't callable
test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, {});
    assert_throws(null, function () { walker.firstChild(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_throws(null, function () { walker.nextNode(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
}, 'Testing with object lacking acceptNode property');

// XXX Servo breaks the test when a callback isn't callable
test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, { acceptNode: "foo" });
    assert_throws(null, function () { walker.firstChild(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_throws(null, function () { walker.nextNode(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
}, 'Testing with object with non-function acceptNode property');

test(function()
{
    var filter = function() { return /*NodeFilter.*/FILTER_ACCEPT; };
    filter.acceptNode = function(node) { return /*NodeFilter.*/FILTER_SKIP; };
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, filter);
    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
    assert_node(walker.nextNode(), { type: Element, id: 'B1' });
}, 'Testing with function having acceptNode function');

test(function()
{
    var filter = {
        acceptNode: function(node) {
            return /*NodeFilter.*/FILTER_ACCEPT;
        }
    };
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT, filter);
    assert_node(walker.firstChild(), { type: Element, id: 'A1' });
}, 'Testing acceptNode callee');

// XXX Looks like Servo is doing something wrong when a callback function throws
test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT,
                                           function(node) {
                                               throw('filter exception');
                                               return /*NodeFilter.*/FILTER_ACCEPT;
                                           });
    assert_throws(null, function () { walker.firstChild(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_throws(null, function () { walker.nextNode(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
}, 'Testing with filter function that throws');

// XXX Looks like Servo is doing something wrong when a callback function throws
test(function()
{
    var walker = document.createTreeWalker(testElement, /*NodeFilter.*/SHOW_ELEMENT,
                                           {
                                               acceptNode : function(node) {
                                                   throw('filter exception');
                                                   return /*NodeFilter.*/FILTER_ACCEPT;
                                               }
                                           });
    assert_throws(null, function () { walker.firstChild(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
    assert_throws(null, function () { walker.nextNode(); });
    assert_node(walker.currentNode, { type: Element, id: 'root' });
}, 'Testing with filter object that throws');

</script>
</body>
</html>
